﻿/* *
 * =============================================================================
 * File Name:               HorizontalWire.cs
 * Programmer:              Ken Watts
 * Creation Date:           8/3/2008
 * =============================================================================
 * Description:             Class that represents a horizontal wire.
 * =============================================================================
 * Modification Date:       8/3/2008 - Initial Revision
 * =============================================================================
 * */

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace DigitalLogicSimulator
{
    public class HorizontalWire : Device
    {
        // Constants
        private const int k_int_num_inputs = 1;
        private const int k_int_input = 0;
        private const double k_initial_top = 190;
        private const double k_initial_left = 23;
        private const double k_wire_highlight_offset = 0.5;

        // Inputs and outputs
        private const double k_in_p1_x = 0;
        private const double k_in_p1_y = 0;
        private const double k_in_p2_x = 10;
        private const double k_in_p2_y = 0;
        private const double k_out_p1_x = 10;
        private const double k_out_p1_y = 0;
        private const double k_out_p2_x = 20;
        private const double k_out_p2_y = 0;

        // Input states
        bool inputState = false;
        bool leftInput = true;

        public HorizontalWire()
        {
            // Create an array for the input devices
            inputDevices = new Device[k_int_num_inputs];
            // Initialize the output state
            outputState = inputState;
        }

        protected override void ReplaceInstance(Panel parentPanel)
        {
            parentPanel.Children.Add(new HorizontalWire());
        }

        protected override void Draw()
        {
            pathGeometry = new PathGeometry();
            pathGeometry.Figures = new PathFigureCollection();
            pathGeometry.FillRule = FillRule.Nonzero;
            PathFigure pathFigure = new PathFigure();
            // Add Input & output
            pathFigure.StartPoint = new Point(k_in_p1_x, k_in_p1_y);
            pathFigure.Segments = new PathSegmentCollection();
            LineSegment ls = new LineSegment();
            ls.Point = new Point(k_out_p2_x, k_out_p2_y);
            pathFigure.Segments.Add(ls);

            pathGeometry.Figures.Add(pathFigure);

            // Setup the path
            path = new Path();
            path.Stretch = Stretch.Fill;
            path.StrokeLineJoin = PenLineJoin.Round;
            path.Stroke = new SolidColorBrush(strokeColor);
            path.Fill = new SolidColorBrush(backgroundColor);
            path.StrokeThickness = k_dbl_stroke_thickness;
            path.Data = pathGeometry;

            path.MouseLeftButtonDown += new MouseButtonEventHandler(CustomMouseLeftButtonDown);
            path.MouseLeftButtonUp += new MouseButtonEventHandler(CustomMouseLeftButtonUp);
            path.MouseMove += new MouseEventHandler(CustomMouseMove);
            path.MouseEnter += new MouseEventHandler(CustomMouseEnter);
            path.MouseLeave += new MouseEventHandler(CustomMouseLeave);

            Children.Add(path);

            // Setup output points
            outputLeft.X = k_out_p1_x;
            outputLeft.Y = k_out_p1_y;
            outputRight.X = k_out_p2_x - 1;
            outputRight.Y = k_out_p2_y;

            // Setup rubberband output line
            outputLine = new Line();
            outputLine.Stroke = new SolidColorBrush(rubberbandColor);
            outputLine.StrokeThickness = k_dbl_stroke_thickness;
            outputLine.Visibility = System.Windows.Visibility.Collapsed;

            Children.Add(outputLine);

            // Set the initial position
            path.SetValue(Canvas.LeftProperty, k_initial_left);
            path.SetValue(Canvas.TopProperty, k_initial_top);
            canvLeft = k_initial_left;
            canvTop = k_initial_top;
        }

        protected override int Connect(Device logicDevice, Point clickedPoint)
        {
            double xScale = 1;
            Panel parent = (Panel)Parent;
            if (Application.Current.Host.Content.IsFullScreen)
                xScale = Application.Current.Host.Content.ActualWidth / parent.ActualWidth;
            // Has only 1 input
            if (inputDevices[k_int_input] == null)
            {
                if ((clickedPoint.X / xScale - canvLeft) < k_out_p1_x)
                    leftInput = true;
                else
                    leftInput = false;
                inputDevices[k_int_input] = logicDevice;
                return k_int_input;
            }
            // Can't connect...
            return k_int_not_connected;
        }

        protected override Point GetInputLocation(int inputNumber)
        {
            Point p = new Point();
            // Figure out the correct point information
            if (inputNumber == k_int_input)
            {
                if (leftInput)
                {
                    p.X = canvLeft + k_in_p1_x + 1;
                    p.Y = canvTop + k_in_p1_y;
                    // Setup output points
                    outputLeft.X = k_out_p1_x;
                    outputLeft.Y = k_out_p1_y;
                    outputRight.X = k_out_p2_x - 1;
                    outputRight.Y = k_out_p2_y;
                }
                else
                {
                    p.X = canvLeft + k_out_p2_x - 1;
                    p.Y = canvTop + k_out_p2_y;
                    // Setup output points
                    outputLeft.X = k_in_p2_x;
                    outputLeft.Y = k_in_p2_y;
                    outputRight.X = k_in_p1_x + 1;
                    outputRight.Y = k_in_p1_y;
                }
            }
            return p;
        }

        protected override void CustomMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            beginX = e.GetPosition(this).X;
            beginY = e.GetPosition(this).Y;
            trackingMouseMove = true;
            moving = false;
            FrameworkElement fe = sender as FrameworkElement;
            canvLeft = Convert.ToDouble(fe.GetValue(Canvas.LeftProperty));
            canvTop = Convert.ToDouble(fe.GetValue(Canvas.TopProperty));
            // Reset the stroke color
            path.Stroke = strokeBrush;
            path.StrokeThickness = k_dbl_stroke_thickness;
            // Assume that this will be selected
            if (initiated)
                selected = !selected;

            if (fe != null)
            {
                fe.CaptureMouse();
            }

            // Reset z-orders
            Panel parentPanel = (Panel)Parent;
            int topMost = 0, zOrder;
            for (int i = 0; i < parentPanel.Children.Count; i++)
                if (parentPanel.Children[i] is Device)
                {
                    Device dev = (Device)parentPanel.Children[i];
                    // Determine the z-order of all item
                    zOrder = (int)dev.GetValue(Canvas.ZIndexProperty);
                    if (zOrder > topMost)
                        topMost = zOrder;
                }
            // Set this above the rest
            SetValue(Canvas.ZIndexProperty, topMost + 1);

            // Get the point that was clicked
            clickedPoint.X = beginX - canvLeft;
            clickedPoint.Y = beginY - canvTop;

            // Determine if output has been clicked
            if ((outputLine != null) && initiated)
            {
                if (leftInput)
                {
                    if (clickedPoint.X > outputLeft.X)
                    {
                        Point outputPoint = GetOutputLocation();
                        beginX = outputPoint.X;
                        beginY = outputPoint.Y;
                        outputLine.X1 = beginX;
                        outputLine.Y1 = beginY;
                        outputLine.X2 = beginX;
                        outputLine.Y2 = beginY;
                        outputLine.Visibility = System.Windows.Visibility.Visible;
                    }
                }
                else
                {
                    if (clickedPoint.X < outputLeft.X)
                    {
                        Point outputPoint = GetOutputLocation();
                        beginX = outputPoint.X;
                        beginY = outputPoint.Y;
                        outputLine.X1 = beginX;
                        outputLine.Y1 = beginY;
                        outputLine.X2 = beginX;
                        outputLine.Y2 = beginY;
                        outputLine.Visibility = System.Windows.Visibility.Visible;
                    }
                }
            }
            // Note that the mouse event has been handled
            e.Handled = true;
        }

        // Sets the state of the specified input to the given value
        public override void SetInputState(int inputNumber, bool state)
        {
            if (inputNumber == k_int_input)
                // Set the first input state
                inputState = state;
            // Set the output state - Prevent infinite feedback
            bool newOutputState = inputState;
            if (newOutputState != outputState)
            {
                outputState = newOutputState;
                // Set the input state of any connected devices
                UpdateOutputState();
            }
        }

        // Returns the output location of the device
        public override Point GetOutputLocation()
        {
            return new Point(outputRight.X + canvLeft, outputRight.Y + canvTop);
        }

        protected override void CustomMouseEnter(object sender, System.Windows.RoutedEventArgs e)
        {
            base.CustomMouseEnter(sender, e);
            if (!initiated)
            {
                FrameworkElement fe = sender as FrameworkElement;
                canvLeft = Convert.ToDouble(fe.GetValue(Canvas.LeftProperty));
                canvTop = Convert.ToDouble(fe.GetValue(Canvas.TopProperty));
                fe.SetValue(Canvas.LeftProperty, canvLeft + k_wire_highlight_offset);
                fe.SetValue(Canvas.TopProperty, canvTop + k_wire_highlight_offset);
            }
        }

        protected override void CustomMouseLeave(object sender, System.Windows.RoutedEventArgs e)
        {
            base.CustomMouseLeave(sender, e);
            if (!initiated)
            {
                FrameworkElement fe = sender as FrameworkElement;
                canvLeft = Convert.ToDouble(fe.GetValue(Canvas.LeftProperty));
                canvTop = Convert.ToDouble(fe.GetValue(Canvas.TopProperty));
                fe.SetValue(Canvas.LeftProperty, canvLeft - k_wire_highlight_offset);
                fe.SetValue(Canvas.TopProperty, canvTop - k_wire_highlight_offset);
            }
        }

        public override void Highlight(bool on)
        {
            if (initiated)
            {
                if (path != null)
                {
                    if (on)
                    {
                        path.Stroke = highlightBrush;
                    }
                    else
                    {
                        path.Stroke = strokeBrush;
                    }
                }
                highlighted = (highlightColor == backgroundColor) ? false : on;
            }
        }

    }
}
